package com.wang.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wang.api.domain.UserExeResult;
import com.wang.api.domain.JudgeSubmitDTO;
import com.wang.api.domain.UserQuestionResultVO;
import com.wang.judge.domain.SandBoxExecuteResult;
import com.wang.judge.domain.UserSubmit;
import com.wang.judge.mapper.UserSubmitMapper;
import com.wang.judge.service.IJudgeService;
import com.wang.judge.service.ISandBoxPoolService;
import com.wang.common.core.constants.Constant;
import com.wang.common.core.constants.JudgeConstants;
import com.wang.common.core.enums.CodeRunStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSON;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandBoxPoolService sandBoxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {

        //到代码沙箱中执行Java代码
        SandBoxExecuteResult sandBoxExecuteResult = sandBoxPoolService.exeJavaCode(
                judgeSubmitDTO.getUserId(), //用户id
                judgeSubmitDTO.getUserCode(), //用户代码
                judgeSubmitDTO.getInputList() //测试输入
        );

        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();

        //判断代码执行结果 并做出相应处理
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())){
            //1.如果运行和编译没有异常 --> 去判断结果是否符合预期
            userQuestionResultVO = doJudge(userQuestionResultVO,sandBoxExecuteResult,judgeSubmitDTO);
        } else {
            //2.如果运行或者是编译发生错误 --> 封装返回结果
            userQuestionResultVO.setPass(Constant.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            if (sandBoxExecuteResult != null){
                //如果错误信息不为空 --> 把代码沙箱中的错误信息设置进入返回结果
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            } else {
                //如果为错误信息为空 --> 表明当前错误是是未知错误,设置UNKNOWN_FAILED
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
        }

        //保存信息到数据库中
        saveUserSubmit(userQuestionResultVO,judgeSubmitDTO);
        return userQuestionResultVO;
    }

    /**
     * 代码运行成功之后 (指的是没有编译错误 或 运行异常)
     * 判断运行结果是否符合预期 并构建返回结果
     * @return 返回UserQuestionResultVO
     */
    private UserQuestionResultVO doJudge(UserQuestionResultVO userQuestionResultVO,
                                         SandBoxExecuteResult sandBoxExecuteResult,
                                         JudgeSubmitDTO judgeSubmitDTO){

        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();

        //如果 运行结果输出个数 和 测试用例输出个数 不相等 --> 不通过
        if (exeOutputList.size() != outputList.size()){
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            userQuestionResultVO.setPass(Constant.FALSE);
            return userQuestionResultVO;
        }

        //用来记录 输入信息+期望输出结果+实际输出结果
        List<UserExeResult> userExeResultList = new ArrayList<>();
        //对比实际输出结果和期望输出结果
        boolean pass = resultCompare(judgeSubmitDTO,outputList,exeOutputList,userExeResultList);
        //构造返回结果
        return assembleUserQuestionResultVO(judgeSubmitDTO,userExeResultList,userQuestionResultVO,sandBoxExecuteResult,pass);
    }

    /**
     * 对比实际输出结果和期望输出结果,并返回输入,实际输出和期望输出
     * @param judgeSubmitDTO 用例输入 (从judgeSubmitDTO中取出其中的输入InputList)
     * @param outputList  期望输出
     * @param exeOutputList 实际输出
     * @param userExeResultList 返回输入,实际输出和期望输出
     * @return 返回是否通过所有用例
     */
    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO,
                                  List<String> outputList,
                                  List<String> exeOutputList,
                                  List<UserExeResult> userExeResultList){
        boolean pass = true;
        //依次对比期望输出结果和实际输出结果
        for (int i = 0;i < outputList.size();i++){

            //获取到需要为userExeResultList填充的信息
            String output = outputList.get(i); //期望输出
            String exeOutput = exeOutputList.get(i); //实际输出
            String input = judgeSubmitDTO.getInputList().get(i); //用例输入

            //填充UserExeResult信息
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutput);
            userExeResultList.add(userExeResult);

            //判断期望输出结果和实际输出结果是否相等
            if (!output.equals(exeOutput)){
                pass = false;
            }
        }
        return pass;
    }

    /**
     * 构造返回结果
     * @param judgeSubmitDTO 从其中获取空间限制和时间限制
     * @param userExeResultList 输入,实际输出,期望输出
     * @param userQuestionResultVO 需要构造的返回结果
     * @param sandBoxExecuteResult 代码沙箱返回的运行结果,需要从其中对比空间限制和时间限制
     * @param pass 是否通过全部测试用例
     * @return 返回构造好的返回结果userQuestionResultVO
     */
    private UserQuestionResultVO assembleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                              List<UserExeResult> userExeResultList,
                                                              UserQuestionResultVO userQuestionResultVO,
                                                              SandBoxExecuteResult sandBoxExecuteResult,
                                                              boolean pass){
        //设置输入,预期输出和实际输出
        userQuestionResultVO.setUserExeResultList(userExeResultList);
        //最终结果,有未通过所有测试用例,超时,超空三中结果
        //未通过全部测试用例
        if (!pass){
            userQuestionResultVO.setPass(Constant.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        //超时
        if (judgeSubmitDTO.getTimeLimit() < sandBoxExecuteResult.getUseTime()){
            userQuestionResultVO.setPass(Constant.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }
        //超空
        if (judgeSubmitDTO.getSpaceLimit() < sandBoxExecuteResult.getUseMemory()){
            userQuestionResultVO.setPass(Constant.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }
        //运行通过
        userQuestionResultVO.setPass(Constant.TRUE);
        //题目得分默认是100 * 题目难度
        userQuestionResultVO.setScore(JudgeConstants.DEFAULT_SCORE * judgeSubmitDTO.getDifficulty());
        userQuestionResultVO.setExeMessage(CodeRunStatus.SUCCEED.getMsg());
        return userQuestionResultVO;
    }

    private void saveUserSubmit(UserQuestionResultVO userQuestionResultVO, JudgeSubmitDTO judgeSubmitDTO){
        UserSubmit userSubmit = new UserSubmit();

        //将userQuestionResultVO的属性值复制到userSubmit中
        BeanUtil.copyProperties(userQuestionResultVO,userSubmit);

        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList())); //需要把运行结果保存到数据库中

        //需要把之前的执行结果删除掉
        try {
            userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                    .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                    .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                    .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                    .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        } catch (Exception e) {
            log.error("删除失败，完整异常堆栈：", e); // 打印最外层异常
            log.error("根本原因：", e.getCause());  // 打印原始异常
            throw e;
        }

        try{
            userSubmitMapper.insert(userSubmit);
        }catch (Exception e){
            log.error("删除失败，完整异常堆栈：", e); // 打印最外层异常
            log.error("根本原因：", e.getCause());  // 打印原始异常
            throw e;
        }
    }
}
